package it.uniroma2.art.coda.pearl.model;

import it.uniroma2.art.coda.structures.depends.DependencyObject;

import java.text.RuleBasedCollator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Andrea Turbati
 */

public class ProjectionRule {

	private String uimaTypeName;
	private String id; // this can be null
	private double confidenceValue; // TODO choose a better name
	private Map<String, PlaceholderStruct> placeholdersMap; // this map contains the placeholders coming from
	// the nodes and the alias part of the projection rule
	private Map<String, BindingStruct> bindingsMap;
	private Collection<GraphElement> graphList;
	private Collection<String> varList;
	private Collection<GraphElement> whereList;
	private Map<String, String> parametersMap;

	// used only for internal reason, should not be used by developers
	private Map<String, Collection<DependencyObject>> tempDependsOnMap;

	// list of all the projection rule this one depends on
	private Collection<String> dependsPrList;

	// map containing the Projection Rules to which this Projection Rule depends on. The Projection Rule is
	// inside the DependsObject with other information
	private Map<String, Collection<DependencyObject>> dependsOnMap;

	// list of all the dependsOn of this projectionRule
	private List<DependencyObject> dependsOnList;

	// map containing the Projection Rules that depends on this Projection Rule
	private Map<String, Collection<ProjectionRule>> referredByMap;
	private boolean laziness;

	// private Map<String, Collection<FeatAndProperty> > featAndPropMap;

	// Add same structure to store information about the possible mapping between some feature on some
	// property.
	// The important thing is that the subject of this property MUST be some information inside the same
	// projection rule

	public ProjectionRule() {
		initialize();
	}

	private void initialize() {
		this.uimaTypeName = null;
		this.confidenceValue = 0;
		this.laziness = false;
		this.id = null;
		this.parametersMap = new HashMap<String, String>();
		this.tempDependsOnMap = new HashMap<String, Collection<DependencyObject>>();
		this.dependsPrList = new ArrayList<String>();
		this.dependsOnMap = new HashMap<String, Collection<DependencyObject>>();
		this.dependsOnList = new ArrayList<DependencyObject>();
		this.referredByMap = new HashMap<String, Collection<ProjectionRule>>();
		this.placeholdersMap = new HashMap<String, PlaceholderStruct>();
		this.bindingsMap = new HashMap<String, BindingStruct>();
		this.graphList = new ArrayList<GraphElement>();
		this.whereList = new ArrayList<GraphElement>();
		this.varList = new ArrayList<String>();
	}

	public boolean isIdNull() {
		return id == null;
	}

	public String getId() {
		return id;
	}

	public String getUIMAType() {
		return uimaTypeName;
	}

	public double getConfidenceValue() {
		return confidenceValue;
	}

	public boolean addPlaceholder(PlaceholderStruct placeholderStruct) {
		String placeholderName = placeholderStruct.getName();
		if (placeholdersMap.containsKey(placeholderName))
			return false;
		placeholdersMap.put(placeholderName, placeholderStruct);
		return true;
	}

	public Map<String, PlaceholderStruct> getPlaceholderMap() {
		return placeholdersMap;
	}

	public void addElementToGraph(GraphElement graphElement) {
		graphList.add(graphElement);
	}
	
	public void addElementsToGraph(Collection<GraphElement> graphElements) {
		graphList.addAll(graphElements);
	}

	public Collection<GraphElement> getGraphList() {
		return graphList;
	}

	public void addElementToWhere(GraphElement graphElement) {
		whereList.add(graphElement);
	}
	
	public void addElementsToWhere(Collection<GraphElement> graphElements) {
		whereList.addAll(graphElements);
	}

	public Collection<GraphElement> getWhereList() {
		return whereList;
	}

	public void addVar(String var) {
		varList.add(var);
	}

	public Collection<String> getVarList() {
		return varList;
	}

	public void addTempDependency(String typeOfDependecy, DependencyObject dependsObj) {
		if (dependsObj == null)
			return;
		if (tempDependsOnMap.containsKey(typeOfDependecy) == false)
			tempDependsOnMap.put(typeOfDependecy, new ArrayList<DependencyObject>());
		Collection<DependencyObject> prIdList = tempDependsOnMap.get(typeOfDependecy);
		prIdList.add(dependsObj);

		if (dependsPrList.contains(dependsObj.getRuleId()) == false)
			dependsPrList.add(dependsObj.getRuleId());
	}

	/*public Collection<String> getDependsPrList() {
		return dependsPrList;
	}*/

	public Map<String, Collection<DependencyObject>> getTempDependencyMap() {
		return tempDependsOnMap;
	}

	public void addDependency(String typeOfDependecy, DependencyObject dependsObj) {
		if (dependsOnMap.containsKey(typeOfDependecy) == false)
			dependsOnMap.put(typeOfDependecy, new ArrayList<DependencyObject>());
		Collection<DependencyObject> dependsObjList = dependsOnMap.get(typeOfDependecy);
		dependsObjList.add(dependsObj);
		dependsOnList.add(dependsObj);
	}

	public Map<String, Collection<DependencyObject>> getDependencyMap() {
		return dependsOnMap;
	}

	public Collection<DependencyObject> getDependencyList() {
		return dependsOnList;
	}

	public void addReferredBy(String typeOfReferrencing, ProjectionRule prRule) {
		if (referredByMap.containsKey(typeOfReferrencing) == false)
			referredByMap.put(typeOfReferrencing, new ArrayList<ProjectionRule>());
		Collection<ProjectionRule> prRuleList = referredByMap.get(typeOfReferrencing);
		prRuleList.add(prRule);
	}

	public Map<String, Collection<ProjectionRule>> getReferredByMap() {
		return referredByMap;
	}

	public boolean addParameter(String attName, String attValue) {
		if (parametersMap.containsKey(attValue))
			return false;
		parametersMap.put(attName, attValue);
		return true;
	}

	public Map<String, String> getParametersMap() {
		return parametersMap;
	}

	public void setLaziness(boolean laziness) {
		this.laziness  = laziness;
	}
	
	public boolean isLazy() {
		return laziness;
	}

	public void setRuleId(String ruleId) {
		this.id = ruleId;
	}

	public void setUimaTypeName(String uimaTypeName) {
		this.uimaTypeName = uimaTypeName;
	}

	public void addBinding(BindingStruct bindingStruct) {
		bindingsMap.put(bindingStruct.getName(), bindingStruct);
	}
	
	public Map<String, BindingStruct> getBindingsMap(){
		return bindingsMap;
	}
}
